package com.hdu.kdk_feign.handler;


import com.hdu.kdk_feign.annotation.FeignClient;
import com.hdu.kdk_feign.annotation.GetMapping;
import com.hdu.kdk_feign.annotation.PostMapping;
import com.hdu.kdk_feign.executor.Executor;
import com.hdu.kdk_feign.executor.HttpExecutor;
import com.hdu.kdk_feign.paramHandler.Param;

import com.hdu.kdk_feign.utils.Assert;

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;


public class FeignInvocationHandler implements InvocationHandler {

    private static final String HTTP = "http://";
    private static final String SEPARATOR = "/";
    private final Map<Method, Executor> METHOD_EXECUTOR = new HashMap<>();


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (method.getDeclaringClass() == Object.class) {
            return method.invoke(this, args);
        }
        Executor executor = METHOD_EXECUTOR.get(method);
        if (executor == null) {
            executor = buildExecutor(method, args);
            METHOD_EXECUTOR.put(method, executor);
        } else {
            executor.refreshParam(args);
        }
        return executor.execute();
    }

    private Executor buildExecutor(Method method, Object[] args) {
        List<Param> paramList = new ArrayList<>();
        for (int i = 0; i < method.getParameters().length; i++) {
            Parameter currentParameter = method.getParameters()[i];
            Param p = new Param();
            p.setParamValue(args[i]);
            p.setParamName(currentParameter.getName());
            Annotation annotation = currentParameter.getAnnotations().length > 0
                    ? currentParameter.getAnnotations()[0]
                    : null;
            p.setParamAnnotation(annotation);
            paramList.add(p);
        }

        HttpExecutor.HTTP_METHOD httpMethod = null;

        if (method.getAnnotation(GetMapping.class) != null) {
            httpMethod = HttpExecutor.HTTP_METHOD.GET;
        }

        if (method.getAnnotation(PostMapping.class) != null) {
            httpMethod = HttpExecutor.HTTP_METHOD.POST;
        }

        return new HttpExecutor(getUrl(method), paramList, method.getReturnType(), httpMethod);
    }

    private String getUrl(Method method) {
        Class<?> targetClass = method.getDeclaringClass();
        FeignClient feignClient = targetClass.getAnnotation(FeignClient.class);
        Assert.isTrue(feignClient == null, "@feignClient annotation must be point");

        GetMapping getMapping = method.getAnnotation(GetMapping.class);
        PostMapping postMapping = method.getAnnotation(PostMapping.class);

        boolean p1 = getMapping == null && postMapping == null;
        boolean p2 = getMapping != null && postMapping != null;

        Assert.isTrue(p1, "GetMapping or PostMapping annotations must be on the method");
        Assert.isTrue(p2, "GetMapping and PostMapping annotations can not be set at the same time");


        String baseUrl = feignClient.url();
        String path = null;
        if (postMapping != null) {
            path = postMapping.value();
        }
        if (getMapping != null) {
            path = getMapping.value();
        }

        return HTTP + baseUrl + SEPARATOR + path;
    }

}
